import { git } from './git/git'
import open from 'open'
import { GitStatus } from './git/git-status'
import dayjs from 'dayjs'
import { resolve } from 'path'
import { applyTransform, config, saveConfigJSON } from './configs'

const gitlabMergeRequest = (
  path: string,
  sourceBranch: string,
  targetBranch: string,
  sourceId: string,
  tagetId: string,
) =>
  `https://gitlab.gz.cvte.cn/${path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=${sourceBranch}&merge_request%5Bsource_project_id%5D=${sourceId}&merge_request%5Btarget_branch%5D=${targetBranch}&merge_request%5Btarget_project_id%5D=${tagetId}`

const mergeRequestUrl = (
  path: string,
  sourceBranch: string,
  targetBranch: string,
  sourceId: string,
  tagetId: string,
) => {
  if (path.includes('gitlab.gz.cvte.cn')) {
    path = path.split('gitlab.gz.cvte.cn/')[1]
    return gitlabMergeRequest(
      path,
      sourceBranch,
      targetBranch,
      sourceId,
      tagetId,
    )
  }
  return gitlabMergeRequest(path, sourceBranch, targetBranch, sourceId, tagetId)
}

export class Branches {
  private _currentBranch: string = ''
  private defaultBackBranch: string = 'master'
  get currentBranch() {
    if (!this._currentBranch) {
      this._currentBranch = git.currentBranch()
    }
    return this._currentBranch
  }
  static branches: Branches | null = null
  constructor() {
    if (Branches.branches) {
      return Branches.branches
    }

    // 自动设置提交前缀
    const currentBranch = this.currentBranch;
    if (currentBranch.indexOf('refs#') !== -1) {
      const redmineId = currentBranch.substring(currentBranch.indexOf('refs#') + 5, currentBranch.indexOf('-'));
      config.prefix = `refs #${redmineId}`
    }

    Branches.branches = this
  }
  getBaseBranch(base: string) {
    return config.base[base] || config.base.default || ''
  }

  /**
   * 依赖 projectInfo.path, projectInfo.projectId
   * @param target 目标分支
   */
  mergeRequest(target?: string) {
    const info = config.projectInfo
    if (!info.projectId || !info.path) {
      console.error("Can't find project info")
      console.log(`you can edit "projectInfo" in file "${resolve('.cm/config.json')}"`)
      return
    }

    const targetBranch = target || config.branches[this.currentBranch]
    if (!targetBranch) {
      console.error("Can't find target branch")
      return
    }

    console.log('Found ProjectInfo:', info)
    console.log('Found TargetBranch:', targetBranch)
    console.log('Opening you browser....')

    git.do(`push --set-upstream origin ${this.currentBranch}`)
    open(
      mergeRequestUrl(
        info.path,
        encodeURIComponent(this.currentBranch),
        encodeURIComponent(targetBranch),
        info.projectId,
        info.projectId,
      ),
    )
  }
  checkoutFromBaseBranch(target: string) { }

  checkoutBranch(target?: string, force?: boolean) {
    if (!target) {
      console.error("Can't find target branch")
      return
    }

    const currentBranch = this.currentBranch;
    git.checkoutBranch(target, force)
    config.branches[target] = currentBranch
    config.createdTime[target] = dayjs().format('YYYY-MM-DD HH:mm')
    saveConfigJSON()
  }

  checkoutBranchWithBaseBranch(
    target: string,
    baseBranch: string,
    force?: boolean,
  ) {
    if (!target) {
      console.error("Can't find target branch")
      return
    }
    if (!baseBranch) {
      console.error("Can't find base branch")
      return
    }

    git.checkoutBranch(target, force)
    git.do(`reset origin/${baseBranch} --hard`)

    config.branches[target] = baseBranch
    config.createdTime[target] = dayjs().format('YYYY-MM-DD HH:mm')
    saveConfigJSON()
  }

  listBranches() {
    const branches = Object.keys(config.branches)
    if (branches.length == 0) {
      console.log('No branches are created by cm')
      return
    }
    branches.sort((a, b) => {
      return config.createdTime[a] < config.createdTime[b] ? -1 : 1
    })
    branches.forEach(branch => {
      console.log(branch)
    })
  }

  removeOldBranches(filter: string, diffTime = 0) {
    const now = Date.now()
    Object.keys(config.createdTime).forEach((k, time) => {
      if (
        k.startsWith(filter) &&
        time &&
        new Date(time).valueOf() < now - diffTime
      ) {
        try {
          this.removeBranch(k)
          this.deleteBranchMark(k)
        } catch (err) {
          this.deleteBranchMark(k)
        }
      }
    })
  }

  deleteBranchMark(target: string) {
    const b = config.branches
    const markedTarget = b[target]
    let deleted = false
    if (markedTarget) {
      // 如果目标分支被删除, 需要转移其他分支对这个分支的依赖.
      Object.keys(b).forEach(k => {
        if (b[k] == target) {
          b[k] = markedTarget
        }
      })
      delete b[target]
      deleted = true
    }

    if (config.createdTime[target]) {
      delete config.createdTime[target]
      deleted = true
    }

    if (deleted) {
      saveConfigJSON()
    }
  }

  removeBranch(target?: string) {
    const b = config.branches

    if (target == this.currentBranch || !target) {
      // 删除当前分支需要先回退分支
      target = this.currentBranch
      if (target == this.defaultBackBranch) {
        console.log(`Can't delete default back branch`)
        return
      }
      const backTarget = b[this.currentBranch] || this.defaultBackBranch
      git.do(`checkout ${backTarget}`)
    }

    git.deleteBranch(target)

    const markedTarget = b[target]
    if (markedTarget) {
      // 如果目标分支被删除, 需要转移其他分支对这个分支的依赖.
      Object.keys(b).forEach(k => {
        if (b[k] == target) {
          b[k] = markedTarget
        }
      })
      delete b[target]
      delete config.createdTime[target]
      saveConfigJSON()
    }
  }

  rebaseParentBranch() {
    const target = config.branches[this.currentBranch]
    if (!target) {
      console.error("Can't find parent branch")
    }

    git.do('fetch')
    git.rebase(`origin/${target}`)
  }

  mergeToBranch(target: string, rebase = false, transform = false) {
    const currentBranch = this.currentBranch
    config.mergingBranch = currentBranch;
    saveConfigJSON();
    git.do(`checkout ${target}`)
    if (rebase) {
      git.do(`fetch`)
      git.do(`rebase origin/${target}`)
    }
    if (transform) {
      const message = applyTransform(currentBranch)
      git.do(`merge ${currentBranch} -m "${message}"`)
    } else {
      git.do(`merge ${currentBranch}`)
    }
    config.mergingBranch = undefined;
    saveConfigJSON();
  }

  mergeMergingBranch() {
    const mergingBranch = config.mergingBranch;
    if (!mergingBranch) { console.log('No merging branch'); return; }
    git.do(`merge ${mergingBranch}`)
    config.mergingBranch = undefined;
    saveConfigJSON();
    return mergingBranch;
  }

  autoCommitAtCurrentBranch(msg: string) {
    const s = new GitStatus()
    if (!s.hasStaged()) {
      git.addAll()
      if (config.excludes && config.excludes.length > 1) {
        git.resetFiles(config.excludes)
      }
    }
    if (config.prefix) {
      msg = config.prefix + ' ' + msg
    }
    git.commit(msg)
  }
}

export const br = new Branches()
